from flask_docs import ApiDoc
from flask import current_app
import json
from config import Config, Dic2Ob
from plugin.logger import MyLogger
import functools
from plugin.resp import MyResponse

logger = MyLogger.logger
docs_config = Config.config_apidoc.apidoc


def get_api_data(self):
    """
    重构flask_docs里面的对应方法，支持重命名树节点
    """
    from flask_docs import logger as apidocs_log, PROJECT_NAME
    data_dict = {}

    blueprints = current_app.config["API_DOC_MEMBER"]
    titles = current_app.config["API_DOC_TITLE"]
    for rule in current_app.url_map.iter_rules():
        # current_app.url_map.iter_rules()是获取当前服务器上所有的路由
        # f = str(rule).split("/")[1]
        f = str(rule)
        f_capitalize = None
        for bp in blueprints:
            if bp.url_prefix and bp.url_prefix in f:
                sort_no = blueprints.index(bp)
                if titles and len(titles) > sort_no:
                    f_capitalize = titles[sort_no]
                else:
                    f_capitalize = bp.url_prefix[1:].replace("/", ".").capitalize()
                break

        if f_capitalize is None:
            continue
        if f_capitalize not in data_dict:
            data_dict[f_capitalize] = {"children": []}

        api = {
            "name": "",
            "name_extra": "",
            "url": "",
            "method": "",
            "doc": "",
            "doc_md": "",
            "router": f_capitalize,
            "api_type": "api",
        }

        try:
            func = current_app.view_functions[rule.endpoint]

            name = self._get_api_name(func)
            req = str(rule)
            url = str(rule)
            method = " ".join([r for r in rule.methods if r in current_app.config["API_DOC_METHODS_LIST"]])
            if method:
                url = "{}\t[{}]".format(url, "\t".join(method.split(" ")))

            result = filter(
                lambda x: x["name"] == name,
                data_dict[f_capitalize]["children"],
            )
            result_list = list(result)
            if len(result_list) > 0:
                result_list[0]["url"] = result_list[0]["url"] + " " + url
                result_list[0]["url"] = " ".join(list(set(result_list[0]["url"].split(" "))))
                result_list[0]["method"] = result_list[0]["method"] + \
                    " " + method
                result_list[0]["method"] = " ".join(list(set(result_list[0]["method"].split(" "))))
                raise RuntimeError

            api["url"] = url
            api["method"] = method

            doc = self._get_api_doc(func)
            title = doc.split('MYTITLE:')[1]
            doc = doc.split('MYTITLE:')[0]

            (
                api["doc"],
                api["name_extra"],
                api["doc_md"],
            ) = self._get_doc_name_extra_doc_md(doc)

            if api["name_extra"] == '':
                api["name"] = title
            else:
                api["name"] = title
                api["name_extra"] = name

            # 自动生成resp文档内容
            # if docs_config and '__resp_example__' in api["doc_md"]:
            #     host = docs_config.localhost or docs_config.publichost
            #     url = "{}/{}".format(host.strip('/'), req.strip('/'))
            #     # start = api["doc_md"].index(publichost)
            #     # end = api["doc_md"].index('```', start)
            #     # url = api["doc_md"][start:end].replace(publichost, docs_config.localhost).strip()
            #     try:
            #         # if not url.endswith('-') and not url.endswith('-\n'):  # 使用-结尾的不做请求
            #         if not api["doc_md"].contains('```cmd\n      -\n    ```'):
            #             resp = requests.request(method, url)
            #             api["doc_md"] = api["doc_md"].replace('__resp_example__', resp.text)
            #     except Exception:
            #         logger.error('使用req样例请求失败，请检测apidoc.host配置以及req样例是否正确，{}'.format(url))

        except Exception as e:
            apidocs_log.exception("{} error - {}".format(PROJECT_NAME, e))
        else:
            data_dict[f_capitalize]["children"].append(api)

        if data_dict[f_capitalize]["children"] == []:
            data_dict.pop(f_capitalize)
        else:
            data_dict[f_capitalize]["children"].sort(key=lambda x: x["name"])

    return data_dict


def beautify_json(jsondata):
    """
    格式化json
    :param jsondata:
    :return:
    """
    try:
        jsondata = json.loads(jsondata)
        jsondata = json.dumps(jsondata, indent=4, ensure_ascii=False)
    except Exception:
        pass
    return jsondata


def analyse_method_doc(doc: str):
    """
    解析方法注释
    :param doc: 方法注释
    :return: params, returns, descr
    """
    sections = doc.split('\n')
    params = {}
    descr = ''
    for sect in sections:
        sect = sect.strip()
        if sect.startswith(':param'):
            _sect = sect.replace(':param', '').strip()
            param = _sect.split(':')
            params[param[0]] = ":".join(param[1:])
        elif sect.startswith(':return'):
            continue
        else:
            descr = descr + sect
    paramstr = '\n'.join(["{}|{}".format(key, value) for key, value in params.items()])
    return paramstr, descr


# @staticmethod
def APIDOC(title: str = None, returns=None):
    """
    生成详细接口文档

    + 解析 :param xxx: 格式，生成请求接口参数
    + 解析 returns 对象的 __init__ 方法注释，生成返回参数
    + 解析 :return: 格式，生成接口返回值
    + 其余的注释生成接口描述
    """

    def decorator(func):
        nonlocal title, returns
        try:
            assert docs_config and docs_config.接口, 'apidoc配置文件缺少apidoc项'
            title = title or func.__name__.lower()

            # 解析方法注释
            # ep:
            # 远程开关门接口
            # :param addr: 设备地址 ep: 1-1#1-1
            # :param time: 开门时长
            # :param optype: 0-远程关门 1-远程开门
            # :return: <MyResponse>
            params, descr = analyse_method_doc(func.__doc__)
            is_list = False
            if type(returns) == list:
                returns = returns[0]
                is_list = True
            resp_args = analyse_method_doc(getattr(returns.__class__, '__init__').__doc__)[0] if returns else {}
            if is_list:
                return_data = MyResponse.Success([returns.__dict__] if returns else None).strip()
            else:
                return_data = MyResponse.Success(returns.__dict__ if returns else None).strip()
            return_data = beautify_json(return_data)

            func_config = Dic2Ob(docs_config.接口[title] if docs_config.接口.__contains__(title) else {})  # 配置文件中接口配置项
            apidesc = func_config.描述 or descr or '-'  # 接口描述, 优先使用配置文件中的, 其次使用方法注释中的
            args = func_config.参数 or params or '-|-'  # 接口参数
            respargs = func_config.返回字段 or resp_args or '-'  # 返回字段描述
            retitle = func_config.标题重命名 or title
            extradescr = docs_config.extradescr or ""  # 额外描述

            docs_config.content = docs_config.content or content
            doc = docs_config.content.format(apidesc, args, respargs, return_data, extradescr)
            func.__doc__ = doc + f"MYTITLE:{retitle}"
        except AssertionError as e:
            logger.error(e)

        @functools.wraps(func)
        def decorated_function(*args, **kw):
            return func(*args, **kw)

        return decorated_function

    return decorator


ApiDoc._get_api_data = get_api_data

content = """
    @@@
    ### 接口描述
    {0}

    ### 可传参数
    | 参数名 | 参数说明 |
    |--|--|
    {1}

    ### 返回字段
    | 字段 | 说明 |
    |--|--|
    {2}

    ### 返回样例
    *仅表现数据结构样式*
    ```json
    {3}
    ```

    {4}
    @@@
"""

if not docs_config.__contains__(content):
    """ 默认以本文件的content作为生成的在线文档的模板，如果配置文件里面有配置apidoc.content，那就以配置文档优先 """
    docs_config['content'] = content
    docs_config = Dic2Ob(docs_config)
